﻿using Lypshare.Net.Server.Service;
using System.ServiceModel;
using System.Timers;
using System;
using System.Net;

namespace Lypshare.Net.Client
{
	/// <summary>
	/// AP client.
	/// </summary>
	/// <exception cref='ArgumentNullException'>
	/// Is thrown when an argument passed to a method is invalid because it is <see langword="null" /> .
	/// </exception>
    public class APClient
    {
        // --------------------------------------------------------
        // Class Variables
        // --------------------------------------------------------


        /// <summary>
        /// The _factory.
        /// </summary>
        private DuplexChannelFactory<IAccessService> _factory = null;


        /// <summary>
        /// The _service.
        /// </summary>
        private IAccessService _service = null;
		
		
		/// <summary>
		/// The _id.
		/// </summary>
		private int _id = 0;
		
		
		/// <summary>
		/// The _address.
		/// </summary>
		private IPAddress _address = null;


        /// <summary>
        /// The _timeout.
        /// </summary>
        private Timer _timeout = null;


        /// <summary>
        /// The _scope.
        /// </summary>
        private String _scope = null;


        /// <summary>
        /// The authed.
        /// </summary>
        private bool _authed = false;
		
		
		/// <summary>
		/// The _active.
		/// </summary>
		private bool _active = true;


        // --------------------------------------------------------
        // Class Properties
        // --------------------------------------------------------
		
		
		/// <summary>
		/// Gets the identifier.
		/// </summary>
		/// <value>
		/// The identifier.
		/// </value>
		public int Id
		{
			get { return _id; }
			
		} // Property: Id
		
		
		/// <summary>
		/// Gets the address.
		/// </summary>
		/// <value>
		/// The address.
		/// </value>
		public IPAddress Address
		{
			get { return _address; }
			
		} // Property: Address
		

        /// <summary>
        /// Gets a value indicating whether this instance has scope.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance has scope; otherwise, <c>false</c>.
        /// </value>
        public bool HasScope
        {
            get { return _scope != null; }

        } // Property: HasScope


        /// <summary>
        /// Gets the scope.
        /// </summary>
        /// <value>
        /// The scope.
        /// </value>
        public String Scope
        {
            get { return _scope; }

        } // Property: Scope
		
		
		/// <summary>
		/// Gets a value indicating whether this instance is authed.
		/// </summary>
		/// <value>
		/// <c>true</c> if this instance is authed; otherwise, <c>false</c>.
		/// </value>
        public bool IsAuthed
        {
            get { return _authed; }

        } // Property: IsAuthed
		
		
		/// <summary>
		/// Gets a value indicating whether this instance is active.
		/// </summary>
		/// <value>
		/// <c>true</c> if this instance is active; otherwise, <c>false</c>.
		/// </value>
		public bool IsActive
		{
			get { return _active; }
			
		} // Property: IsActive


        // --------------------------------------------------------
        // Class Functions
        // --------------------------------------------------------


        /// <summary>
        /// Initializes a new instance of the <see cref="Lypshare.Net.Client.APClient"/> class.
        /// </summary>
        /// <param name='address'>
        /// Address.
        /// </param>
        /// <param name='port'>
        /// Port.
        /// </param>
        /// <param name='name'>
        /// Name.
        /// </param>
        /// <param name='callback'>
        /// Callback.
        /// </param>
        /// <exception cref='ArgumentNullException'>
        /// Is thrown when an argument passed to a method is invalid because it is <see langword="null" /> .
        /// </exception>
        public APClient(String address, int port, String name, 
            IAccessServiceCallback callback)
        {
            if (callback == null) throw new ArgumentNullException("callback");
            if (address == null) throw new ArgumentNullException("address");
            if (name == null) throw new ArgumentNullException("name");

            _factory = new DuplexChannelFactory<IAccessService>(
                  callback, new NetTcpBinding(SecurityMode.None), new EndpointAddress(
                     "net.tcp://" + address + ":" + port + "/" + name));
			
            _service = _factory.CreateChannel();
			
			// Register on service
            _id = _service.Register(
				out _address, out _authed);

            // Initialize timer in order
            // to keep connection open
            _timeout = new Timer(3000);

            // Link timeout event
            _timeout.Elapsed += 
                OnTimeoutElapsed;

            // Timeout properties
            _timeout.AutoReset = true;
            _timeout.Enabled = true;

        }  // Constructor


        /// <summary>
        /// Auth the specified username and password.
        /// </summary>
        /// <param name='username'>
        /// If set to <c>true</c> username.
        /// </param>
        /// <param name='password'>
        /// If set to <c>true</c> password.
        /// </param>
        /// <exception cref='ArgumentNullException'>
        /// Is thrown when an argument passed to a method is invalid because it is <see langword="null" /> .
        /// </exception>
        public bool Auth(String username, String password)
        {
            if (username == null) throw new ArgumentNullException("username");
            if (password == null) throw new ArgumentNullException("password");

            _authed = _service.Auth(
                username, password);

            return _authed;

        } // Auth


		/// <summary>
		/// Join the specified scope.
		/// </summary>
		/// <param name='scope'>
		/// Scope.
		/// </param>
		/// <exception cref='ArgumentNullException'>
		/// Is thrown when an argument passed to a method is invalid because it is <see langword="null" /> .
		/// </exception>
        public void Join(String scope)
        {
            if (scope == null)
                throw new ArgumentNullException("scope");

            _service.Join(scope);

        } // Join


        /// <summary>
        /// Syncronize the specified connection and port.
        /// </summary>
        /// <param name='connection'>
        /// Connection.
        /// </param>
        /// <param name='port'>
        /// Port.
        /// </param>
        /// <exception cref='ArgumentNullException'>
        /// Is thrown when an argument passed to a method is invalid because it is <see langword="null" /> .
        /// </exception>
        public int Syncronize(IConnection connection, int port)
        {
            if (connection == null)
                throw new ArgumentNullException("connection");

            return _service.Syncronize(
                connection.ClientId, port);

        } // Syncronize


        /// <summary>
        /// Disconnect this instance.
        /// </summary>
        public void Disconnect()
        {
			_active = false;
			
            _timeout.Stop();

            _service.Disconnect();

            _service = null;

            _factory.Abort();
            _factory = null;

        } // Disconnect


        // --------------------------------------------------------
        // Class Handles
        // --------------------------------------------------------

        
        /// <summary>
        /// Raises the timeout elapsed event.
        /// </summary>
        /// <param name='sender'>
        /// Sender.
        /// </param>
        /// <param name='e'>
        /// E.
        /// </param>
        private void OnTimeoutElapsed(object sender, ElapsedEventArgs e)
        {
            // Keep service connection open
            _service.KeepConnection();

        } // OnTimeoutElapsed

    } // class APClient
} // namespace Lypshare.Net.Client